package handler

import (
	"account_project/dao"
	"account_project/models"
	"account_project/service"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	redis2 "github.com/go-redis/redis/v8"
	"gitlab.whjzjx.cn/shanghai/kit/db"
	"gitlab.whjzjx.cn/shanghai/kit/ginhelper"
	"gitlab.whjzjx.cn/shanghai/kit/redis"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"sync"
	"time"
)

type Handler struct {
	mu               sync.Mutex
	failedLoginCount map[string]int
}

func (h Handler) Hello(ctx *gin.Context, req *models.HelloReq) (*models.HelloResp, error) {
	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}
	v, err := client.Get(ctx, "key").Result()
	if err != nil {
		return nil, err
	}
	return &models.HelloResp{
		Reply: fmt.Sprintf("%s %s", v, req.Name),
	}, nil
}

func (h Handler) List(ctx *gin.Context, empty *ginhelper.Empty) (*models.ListResp, error) {
	res, err := dao.GetALl(ctx)
	if err != nil {
		return nil, err
	}

	return &models.ListResp{
		List: res,
	}, nil
}

func (h Handler) SignIn(ctx *gin.Context, req *models.SignInReq) (*models.SignInResp, error) {
	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}

	// 获取当前日期
	//today := time.Now().Format("2006.01.02")
	today := "2024.05.03"

	// 在 Redis 中设置用户的签到信息
	err = client.HSet(ctx, "signin-"+strconv.Itoa(req.UserId), today, 1).Err()
	if err != nil {
		return nil, err
	}

	return &models.SignInResp{
		Reply: fmt.Sprintf("签到成功"),
	}, nil
}

func (h Handler) CheckIn(ctx *gin.Context, req *models.CheckInReq) (*models.CheckInResp, error) {
	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}

	signins, err := client.HGetAll(ctx, "signin-"+strconv.Itoa(req.UserId)).Result()
	if err != nil {
		return nil, err
	}

	return &models.CheckInResp{
		CheckIn: signins,
	}, nil

}

func (h Handler) CheckIn2(ctx *gin.Context, req *models.CheckInReq2) (*models.CheckInResp2, error) {
	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}

	_, err = client.HGet(ctx, "signin-"+strconv.Itoa(req.UserId), req.Date).Result()
	if err != nil {
		if errors.Is(err, redis2.Nil) {
			return &models.CheckInResp2{
				CheckIn: false,
			}, nil
		}
		return nil, err
	}

	return &models.CheckInResp2{
		CheckIn: true,
	}, nil

}

//bitmap类型实现签到功能

// 查询哪天是否签到

func (h Handler) CheckIn3(ctx *gin.Context, req *models.CheckInReq3) (*models.CheckInResp3, error) {
	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}

	var offset int64

	date, _ := time.Parse("2006.01.02", req.Date)
	sdate, _ := time.Parse("2006.01.02", "2024.05.01")
	// 计算时间差
	duration := date.Sub(sdate)

	// 将时间差转换为天数
	days := int64(duration.Hours() / 24)
	offset = days

	signigns, err := client.GetBit(ctx, "bitmapsarestrings-"+strconv.Itoa(req.UserId), offset).Result()
	if err != nil {
		return nil, err
	}

	return &models.CheckInResp3{
		CheckIn: signigns,
	}, nil

}

// bitmap实现签到功能

func (h Handler) SignIn2(ctx *gin.Context, req *models.SignInReq3) (*models.SignInResp3, error) {
	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}
	var offset int64

	date, err := time.Parse("2006.01.02", req.Date)
	if err != nil {
		return nil, err
	}

	//从2024.05.01为第一天开始计算

	startDate, err := time.Parse("2006.01.02", "2024.05.01")
	if err != nil {
		return nil, err
	}
	// 计算时间差
	duration := date.Sub(startDate)

	// 将时间差转换为天数
	days := int64(duration.Hours() / 24)
	offset = days

	// 在 Redis 中设置用户的签到信息
	err = client.SetBit(ctx, "bitmapsarestrings-"+strconv.Itoa(req.UserId), offset, 1).Err()
	if err != nil {
		return nil, err
	}

	return &models.SignInResp3{
		Reply: fmt.Sprintf("签到成功"),
	}, nil

}

//计算总签到次数

func (h Handler) CheckInTotalNum(ctx *gin.Context, req *models.CheckInTotalNumReq) (*models.CheckInTotalNumResp, error) {

	client, err := redis.GetClient("account")
	if err != nil {
		return nil, err
	}

	signins, err := client.BitCount(ctx, "bitmapsarestrings-"+strconv.Itoa(req.UserId), (*redis2.BitCount)(&req.BitC)).Result()
	if err != nil {
		return nil, err
	}
	return &models.CheckInTotalNumResp{
		Reply: signins,
	}, err
}

//登录接口

func (h *Handler) LogIn(ctx *gin.Context, req *models.LoginRequestReq) (*models.LoginRequestResp, error) {

	client, err := db.GetClient("demo", "master")
	if err != nil {
		return nil, err
	}

	h.mu.Lock()
	defer h.mu.Unlock()

	if h.failedLoginCount == nil {
		h.failedLoginCount = make(map[string]int)
	}

	if dao.Login(req.Name, req.Password, client) {
		// 登录成功，将该用户对应的登录失败次数清零
		h.failedLoginCount[req.Name] = 0
		return &models.LoginRequestResp{
			Reply: "登录成功",
		}, nil
	} else {
		// 登录失败，将该用户对应的登录失败次数加一
		h.failedLoginCount[req.Name]++

		// 判断该用户的登录失败次数是否达到最大值
		if h.failedLoginCount[req.Name] >= 3 {
			// 锁定该用户的账号 1 分钟
			lockUntil := time.Now().Add(time.Minute)
			// 保存锁定时间戳到数据库中，或者设置锁定标志位为 true

			// 返回登录失败和锁定信息
			return &models.LoginRequestResp{
				Reply: fmt.Sprintf("Login failed. Your account has been locked until %s.", lockUntil.Format(time.RFC3339)),
			}, err

		} else {
			return &models.LoginRequestResp{
				Reply: fmt.Sprintf("登录失败！"),
			}, err
		}

	}
}

//注册接口

func (h Handler) Register(ctx *gin.Context, req *models.User) (*models.RegisterRequestResp, error) {

	client, err := db.GetClient("demo", "master")
	if err != nil {
		return nil, err
	}

	var existingUser models.User

	result := client.Where("Name = ?", req.Name).First(&existingUser)
	if result.Error == nil {
		return &models.RegisterRequestResp{
			Reply: "注册失败，用户已经存在！",
		}, err
	} else {
		// 使用MD5哈希密码
		req.Password = service.GenerateMD5(req.Password)
		client.Create(&req)

		return &models.RegisterRequestResp{
			Reply: "注册成功！",
		}, err
	}

}

// 上传头像

func (h *Handler) UploadAvatar(ctx *gin.Context) {
	// 解析表单，获取用户名和文件
	var req models.AvatarUploadReq
	if err := ctx.ShouldBind(&req); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	file, err := ctx.FormFile("file")
	if err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": "未收到文件"})
		return
	}

	// 打开文件
	src, err := file.Open()
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "无法打开文件"})
		return
	}
	defer src.Close()

	// 读取文件内容
	fileBytes, err := io.ReadAll(src)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "无法读取文件"})
		return
	}

	// 确保目录存在
	if err := os.MkdirAll("avatars", os.ModePerm); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建保存目录"})
		return
	}

	// 创建一个唯一的文件名和保存路径
	filename := fmt.Sprintf("%s_%d%s", req.Name, time.Now().UnixNano(), filepath.Ext(file.Filename))
	savePath := filepath.Join("avatars", filename)

	// 保存文件到指定路径
	if err := os.WriteFile(savePath, fileBytes, 0644); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存文件"})
		return
	}

	// 将文件路径保存到数据库
	client, err := db.GetClient("demo", "master")
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "数据库连接失败"})
		return
	}

	if err := dao.SaveAvatarPath(req.Name, savePath, client); err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存文件路径到数据库"})
		return
	}

	// 返回响应
	ctx.JSON(http.StatusOK, models.AvatarUploadResp{
		Reply: "头像上传成功",
		URL:   savePath,
	})
}
